﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;
using System.Security;
using System.Diagnostics;
using System.ComponentModel;
using System.IO;

/// <summary>
///RSATools 的摘要说明
/// </summary>
public class RsaTools
{
    public static Dictionary<string, RSACryptoServiceProvider> DicRsaPrivateKeys = new Dictionary<string, RSACryptoServiceProvider>();
    /// <summary>
    /// 解密方法
    /// </summary>
    /// <param name="strPivKey">私钥</param>
    /// <param name="encryptStr">加密串</param>
    /// <returns>解密结果</returns>
    public static string Decrypt(string strPivKey, string encryptStr)
    {
        RSACryptoServiceProvider rsa = null;
        if (!DicRsaPrivateKeys.Keys.Contains(strPivKey))
        {
            rsa = DecodeRSAPrivateKey(DecodeOpenSSLPrivateKey(strPivKey));
            DicRsaPrivateKeys.Add(strPivKey, rsa);
        }
        else
        {
            rsa = DicRsaPrivateKeys[strPivKey];
        }
        var bData = Convert.FromBase64String(encryptStr);
        var newData = rsa.Decrypt(bData, false);
        return Encoding.UTF8.GetString(newData);
    }

    #region OPENSLL 密钥转换
    public static byte[] DecodeOpenSSLPrivateKey(String instr)
    {
        const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
        const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
        String pemstr = instr.Trim();
        byte[] binkey;
        if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter))
            return null;

        StringBuilder sb = new StringBuilder(pemstr);
        sb.Replace(pemprivheader, "");  //remove headers/footers, if present
        sb.Replace(pemprivfooter, "");

        String pvkstr = sb.ToString().Trim();	//get string after removing leading/trailing whitespace

        try
        {        // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
            binkey = Convert.FromBase64String(pvkstr);
            return binkey;
        }
        catch (System.FormatException)
        {		//if can't b64 decode, it must be an encrypted private key
            //Console.WriteLine("Not an unencrypted OpenSSL PEM private key"); 

            return null;
        }
    }

    public static byte[] DecodeOpenSSLPublicKey(String instr)
    {
        const String pempubheader = "-----BEGIN RSA PUBLIC KEY-----";
        const String pempubfooter = "-----END RSA PUBLIC KEY-----";
        String pemstr = instr.Trim();
        byte[] binkey;
        if (!pemstr.StartsWith(pempubheader) || !pemstr.EndsWith(pempubfooter))
            return null;
        StringBuilder sb = new StringBuilder(pemstr);
        sb.Replace(pempubheader, "");  //remove headers/footers, if present
        sb.Replace(pempubfooter, "");
        sb.Replace("\r\n", "");

        String pubstr = sb.ToString().Trim();	//get string after removing leading/trailing whitespace

        try
        {
            binkey = Convert.FromBase64String(pubstr);
        }
        catch (System.FormatException)
        {		//if can't b64 decode, data is not valid
            return null;
        }
        return binkey;
    }

    //------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider  ---
    public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
    {
        byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;

        // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------
        MemoryStream mem = new MemoryStream(privkey);
        BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
        byte bt = 0;
        ushort twobytes = 0;
        int elems = 0;
        try
        {
            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130)	//data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();	//advance 1 byte
            else if (twobytes == 0x8230)
                binr.ReadInt16();	//advance 2 bytes
            else
                return null;

            twobytes = binr.ReadUInt16();
            if (twobytes != 0x0102)	//version number
                return null;
            bt = binr.ReadByte();
            if (bt != 0x00)
                return null;


            //------  all private key components are Integer sequences ----
            elems = GetIntegerSize(binr);
            MODULUS = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            E = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            D = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            P = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            Q = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            DP = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            DQ = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            IQ = binr.ReadBytes(elems);

            //Console.WriteLine("showing components ..");
            //showBytes("\nModulus", MODULUS);
            //showBytes("\nExponent", E);
            //showBytes("\nD", D);
            //showBytes("\nP", P);
            //showBytes("\nQ", Q);
            //showBytes("\nDP", DP);
            //showBytes("\nDQ", DQ);
            //showBytes("\nIQ", IQ);

            // ------- create RSACryptoServiceProvider instance and initialize with public key -----
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSAParameters RSAparams = new RSAParameters();
            RSAparams.Modulus = MODULUS;
            RSAparams.Exponent = E;
            RSAparams.D = D;
            RSAparams.P = P;
            RSAparams.Q = Q;
            RSAparams.DP = DP;
            RSAparams.DQ = DQ;
            RSAparams.InverseQ = IQ;
            RSA.ImportParameters(RSAparams);
            return RSA;
        }
        catch (Exception)
        {
            return null;
        }
        finally { binr.Close(); }
    }

    private static int GetIntegerSize(BinaryReader binr)
    {
        byte bt = 0;
        byte lowbyte = 0x00;
        byte highbyte = 0x00;
        int count = 0;
        bt = binr.ReadByte();
        if (bt != 0x02)		//expect integer
            return 0;
        bt = binr.ReadByte();

        if (bt == 0x81)
            count = binr.ReadByte();	// data size in next byte
        else
            if (bt == 0x82)
            {
                highbyte = binr.ReadByte();	// data size in next 2 bytes
                lowbyte = binr.ReadByte();
                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                count = BitConverter.ToInt32(modint, 0);
            }
            else
            {
                count = bt;		// we already have the data size
            }



        while (binr.ReadByte() == 0x00)
        {	//remove high order zeros in data
            count -= 1;
        }
        binr.BaseStream.Seek(-1, SeekOrigin.Current);		//last ReadByte wasn't a removed zero, so back up a byte
        return count;
    }

    private static void showBytes(String info, byte[] data)
    {
        Console.WriteLine("{0}  [{1} bytes]", info, data.Length);
        for (int i = 1; i <= data.Length; i++)
        {
            Console.Write("{0:X2}  ", data[i - 1]);
            if (i % 16 == 0)
                Console.WriteLine();
        }
        Console.WriteLine("\n\n");
    }
    #endregion
}